package com.lean.android.ad.core

import android.app.Activity
import android.view.ViewGroup
import android.widget.Toast
import androidx.lifecycle.ProcessLifecycleOwner
import androidx.lifecycle.lifecycleScope
import com.lean.android.ad.core.feed.FeedAd
import com.lean.android.ad.core.feed.FeedAdLoader
import com.lean.android.ad.core.feed.FeedAdRenderer
import com.lean.android.ad.core.interstitial.InterstitialAdLoader
import com.lean.android.ad.core.interstitial.InterstitialAdRenderer
import com.lean.android.ad.core.reward.RewardAdLoader
import com.lean.android.ad.core.reward.RewardAdRenderer
import com.lean.android.ad.core.splash.SplashAdLoader
import com.lean.android.ad.core.splash.SplashRender
import com.lean.android.ad.utils.AdLog
import com.lean.android.ad.utils.DisplayHelper
import kotlinx.coroutines.launch
import java.util.WeakHashMap

object AdLoaders {

    private val appScope by lazy {
        ProcessLifecycleOwner.get().lifecycleScope
    }

    private val adPool = mutableListOf<Ad>()

    private val autoReleaseMap = WeakHashMap<Activity, AdAutoRelease>()

    internal fun toast(activity: Activity, msg: String) {
        activity.runOnUiThread {
            Toast.makeText(activity, msg, Toast.LENGTH_SHORT).show()
        }
    }

    fun startSplashAd(
        activity: Activity,
        adContainer: ViewGroup,
        isColdLaunch: Boolean = true,
        onAdLoaded: (ad: Ad) -> Unit = {},
        onAdShow: () -> Unit = {},
        onAdFinished: () -> Unit = {}
    ) {
        if (!AdSdk.isSdkInit()) {
            AdLog.d(AdLog.AdLoadTag, "Sdk is not init, just return. ")
            onAdFinished()
            return
        }

        if (AdSdk.isVip) {
            AdLog.d(
                AdLog.AdLoadTag, "load splash ad , isVip true , skip load ad.}"
            )
            onAdFinished()
            return
        }

        loadAndRenderAd(activity,
            adLoader = SplashAdLoader(isColdLaunch, adContainer),
            renderer = SplashRender(adContainer),
            renderCallback = object : AdRenderCallback {

                override fun onAdShow(ad: Ad, w: Int, h: Int) {
                    super.onAdShow(ad, w, h)
                    activity.runOnUiThread {
                        onAdShow()
                    }

                }

                override fun onAdShowFail(code: Int, message: String?) {
                    super.onAdShowFail(code, message)
                    activity.runOnUiThread {
                        onAdFinished()
                    }
                }

                override fun onAdClosed(ad: Ad) {
                    activity.runOnUiThread {
                        onAdFinished()
                    }
                }
            },
            onAdLoaded,
            onAdLoadFailed = {
                onAdFinished()
            })
    }


    private fun getOrCreateAutoRelease(activity: Activity): AdAutoRelease {
        return autoReleaseMap.getOrElse(activity) {
            val adAutoRelease = AdAutoRelease(activity) { host ->
                AdLog.d(
                    AdLog.AdAutoRelease,
                    "${host.javaClass.name} onRelease called, remove from autoReleaseMap"
                )
                autoReleaseMap.remove(host)
            }
            autoReleaseMap[activity] = adAutoRelease
            adAutoRelease
        }
    }


    /**
     * 移除当前Activity，展示过的信息流广告
     */
    fun removeCurrentFeedAd(activity: Activity) {
        val autoRelease = autoReleaseMap[activity] ?: return
        autoRelease.release()
    }

    /**
     * 信息流广告加载并展示
     */
    @JvmStatic
    fun startFeedAd(
        activity: Activity,
        adContainer: ViewGroup,
        onAdLoaded: (ad: Ad) -> Unit = {},
        onAdShown: (ad: Ad, w: Int, h: Int) -> Unit = { _, _, _ -> },
        onAdFinished: () -> Unit = {}
    ) {
        if (!AdSdk.isSdkInit()) {
            AdLog.d(AdLog.AdLoadTag, "Sdk is not init, just return. ")
            onAdFinished()
            return
        }

        if (AdSdk.isVip) {
            AdLog.d(
                AdLog.AdLoadTag, "load feed ad , isVip true , skip load ad.}"
            )
            onAdFinished()
            return
        }

        val showFeedAd = fun(
            activity: Activity, adContainer: ViewGroup
        ) {
            var w = adContainer.width - adContainer.paddingStart - adContainer.paddingRight
            if (w <= 0) {
                w = DisplayHelper.getScreenWidth(activity)
            }

            AdLog.d(
                AdLog.AdFeedLoadTag, "startFeedAd calculated width $w, screenWidth = ${
                    DisplayHelper.getScreenWidth(activity)
                }"
            )
            val adAutoRelease = getOrCreateAutoRelease(activity)
            loadAndRenderAd(activity = activity,
                adLoader = FeedAdLoader(w),
                renderer = FeedAdRenderer(activity, adContainer),
                renderCallback = object : AdRenderCallback {

                    override fun onAdShow(ad: Ad, w: Int, h: Int) {
                        onAdShown(ad, w, h)
                        adAutoRelease.addAd(ad)
                    }

                    override fun onAdClosed(ad: Ad) {
                        onAdFinished()
                        adAutoRelease.removeAd(ad)
                    }
                },
                onAdLoaded = onAdLoaded,
                onAdLoadFailed = {
                    onAdFinished()
                })
        }

        if (adContainer.isLaidOut) {
            showFeedAd(activity, adContainer)
        } else {
            adContainer.post {
                showFeedAd(activity, adContainer)
            }
        }
    }

    /**
     * 加载信息流广告，不展示
     */
    @JvmStatic
    fun loadFeedAd(
        activity: Activity,
        expectedWidth: Int = 0,
        onAdLoaded: (ad: Ad) -> Unit = {},
        onAdLoadFailed: (code: Int, message: String?) -> Unit = { _, _ -> }
    ) {
        if (!AdSdk.isSdkInit()) {
            AdLog.d(AdLog.AdLoadTag, "Sdk is not init, just return. ")
            onAdLoadFailed(ErrorCode.ErrorRewardAdSdkNotInit, "sdk is not init")
            return
        }
        if (AdSdk.isVip) {
            AdLog.d(
                AdLog.AdLoadTag, "load feed ad , isVip true , skip load ad.}"
            )
            onAdLoadFailed(ErrorCode.ErrorSkipAd, "isVip , skip ad load.")
            return
        }

        if (!AdSdk.showAd()) {
            AdLog.d(
                AdLog.AdLoadTag, "load feed ad , isVip true , skip load ad.}"
            )
            onAdLoadFailed(ErrorCode.ErrorSkipAd, "will not show ad .")
            return
        }

        appScope.launch {
            var w = expectedWidth
            if (w <= 0) {
                w = DisplayHelper.getScreenWidth(activity)
            }
            val adLoader = FeedAdLoader(w)

            when (val loadState = adLoader.load(activity)) {
                is AdLoadState.Success -> {
                    val ad = loadState.ad
                    onAdLoaded(ad)
                }

                is AdLoadState.Error -> {
                    onAdLoadFailed(loadState.code, loadState.message)
                }
            }
        }
    }

    /**
     * 模版信息流
     */
    @JvmStatic
    fun renderFeedAd(
        activity: Activity,
        adContainer: ViewGroup,
        ad: Ad,
        onAdShown: (ad: Ad, w: Int, h: Int) -> Unit = { _, _, _ -> },
        onAdFinished: () -> Unit = {}
    ) {
        val adAutoRelease = getOrCreateAutoRelease(activity)
        val adRenderCallback = object : AdRenderCallback {
            override fun onAdShow(ad: Ad, w: Int, h: Int) {
                onAdShown(ad, w, h)
                adAutoRelease.addAd(ad)
            }

            override fun onAdClosed(ad: Ad) {
                onAdFinished()
                adAutoRelease.removeAd(ad)
            }
        }
        if (ad is FeedAd) {
            FeedAdRenderer(activity, adContainer).render(ad, adRenderCallback)
        }

    }


    fun preloadInterstitialAd(
        activity: Activity
    ) {
        if (!AdSdk.isSdkInit()) {
            return
        }
        if (!AdSdk.showAd()) {
            return
        }
        AdLog.d(AdLog.AdLoadTag, "start to preload InterstitialAd")
        appScope.launch {
            val adLoader = InterstitialAdLoader(fullAd = false)
            when (val loadState = adLoader.load(activity)) {
                is AdLoadState.Success -> {
                    val ad = loadState.ad
                    adPool.add(ad)
                    AdLog.d(AdLog.AdLoadTag, "preload InterstitialAd success.")
                }

                else -> {
                    AdLog.d(AdLog.AdLoadTag, "preload InterstitialAd failed.")
                }
            }
        }
    }

    fun startInterstitialAd(
        activity: Activity,
        onAdLoaded: (ad: Ad) -> Unit = {},
        onAdShow: () -> Unit = {},
        onAdFinished: () -> Unit = {}
    ) {
        if (!AdSdk.isSdkInit()) {
            AdLog.d(AdLog.AdLoadTag, "Sdk is not init, just return. ")
            onAdFinished()
            return
        }

        if (AdSdk.isVip) {
            AdLog.d(
                AdLog.AdLoadTag, "load interstitial ad , isVip true , skip load ad.}"
            )
            onAdFinished()
            return
        }

        loadAndRenderAd(activity = activity,
            adLoader = InterstitialAdLoader(false),
            renderer = InterstitialAdRenderer(activity),
            renderCallback = object : AdRenderCallback {

                override fun onAdShow(ad: Ad, w: Int, h: Int) {
                    super.onAdShow(ad, 0, 0)
                    activity.runOnUiThread {
                        onAdShow()
                    }
                }

                override fun onAdClosed(ad: Ad) {
                    activity.runOnUiThread {
                        onAdFinished()
                    }
                }
            },
            onAdLoaded,
            onAdLoadFailed = {
                onAdFinished()
            }

        )

    }

    fun startRewardAd(
        activity: Activity,
        onAdShow: () -> Unit = {},
        onReward: () -> Unit = {},
        onInvalid: (errorCode: Int) -> Unit = {}
    ) {
        if (!AdSdk.isSdkInit()) {
            AdLog.d(AdLog.AdLoadTag, "Sdk is not init, just return. ")
            onInvalid(ErrorCode.ErrorRewardAdInvalid)
            return
        }
        if (AdSdk.isVip) {
            AdLog.d(
                AdLog.AdLoadTag, "load reward ad , isVip true , skip load ad.}"
            )
            onReward()
            return
        }

        if (!AdSdk.showAd()) {
            AdLog.d(
                AdLog.AdLoadTag,
                "Ad will not load, isVip ${AdSdk.isVip} "
            )
            return
        }
        loadAndRenderAd(activity = activity,
            adLoader = RewardAdLoader(),
            renderer = RewardAdRenderer(activity),
            renderCallback = object : RewardAdRenderCallback {
                override fun onAdClosed(ad: Ad) {
                    //do nothing.
                }

                override fun onAdShow(ad: Ad, w: Int, h: Int) {
                    activity.runOnUiThread {
                        onAdShow()
                    }
                }

                override fun onReward() {
                    activity.runOnUiThread {
                        onReward()
                    }
                }

                override fun onInvalid(code: Int) {
                    activity.runOnUiThread {
                        if (code == ErrorCode.Success) {
                            onReward()
                        } else {
                            onInvalid(code)
                        }
                    }
                }
            },
            onAdLoaded = {},
            onAdLoadFailed = {
                if (it == ErrorCode.Success) {
                    onReward()
                } else {
                    onInvalid(it)
                }
            })
    }

    private inline fun <reified AD : Ad, RenderCallback : AdRenderCallback> loadAndRenderAd(
        activity: Activity,
        adLoader: AdLoader,
        renderer: AdRenderer<AD, RenderCallback>,
        renderCallback: RenderCallback,
        crossinline onAdLoaded: (ad: AD) -> Unit,
        crossinline onAdLoadFailed: (errorCode: Int) -> Unit
    ) {

        val adFromPool = adPool.lastOrNull {
            it is AD
        }

        if (adFromPool != null) {
            AdLog.d(AdLog.AdLoadTag, "load ad success from adPool. ")
            val ad = adFromPool as AD
            onAdLoaded(ad)
            renderer.render(ad, renderCallback)
            adPool.remove(adFromPool)
        } else {
            AdLog.d(AdLog.AdLoadTag, "start to load ad . ")
            appScope.launch {
                when (val loadState = adLoader.load(activity)) {
                    is AdLoadState.Success -> {
                        val ad = loadState.ad as AD
                        onAdLoaded(ad)
                        renderer.render(ad, renderCallback)
                    }

                    is AdLoadState.Error -> {
                        onAdLoadFailed(loadState.code)
                    }
                }
            }
        }


    }

}